//! Console控制台导出器
//! 
//! 将追踪数据以易读格式输出到控制台

use crate::*;
use async_trait::async_trait;
use std::time::UNIX_EPOCH;

/// Console导出器
pub struct ConsoleExporter {
    show_details: bool,
    color_enabled: bool,
}

impl ConsoleExporter {
    /// 创建新的Console导出器
    pub fn new() -> Self {
        Self {
            show_details: true,
            color_enabled: true,
        }
    }

    /// 设置是否显示详细信息
    pub fn with_details(mut self, show_details: bool) -> Self {
        self.show_details = show_details;
        self
    }

    /// 设置是否启用颜色
    pub fn with_color(mut self, color_enabled: bool) -> Self {
        self.color_enabled = color_enabled;
        self
    }

    /// 格式化时间戳
    fn format_timestamp(&self, timestamp: std::time::SystemTime) -> String {
        if let Ok(duration) = timestamp.duration_since(UNIX_EPOCH) {
            let secs = duration.as_secs();
            let nanos = duration.subsec_nanos();
            format!("{}.{:09}", secs, nanos)
        } else {
            "invalid_time".to_string()
        }
    }

    /// 格式化持续时间
    fn format_duration(&self, duration_ms: Option<f64>) -> String {
        match duration_ms {
            Some(ms) if ms < 1000.0 => format!("{:.2}ms", ms),
            Some(ms) if ms < 60000.0 => format!("{:.2}s", ms / 1000.0),
            Some(ms) => format!("{:.2}m", ms / 60000.0),
            None => "ongoing".to_string(),
        }
    }

    /// 应用颜色
    fn colorize(&self, text: &str, color: &str) -> String {
        if self.color_enabled {
            match color {
                "red" => format!("\x1b[31m{}\x1b[0m", text),
                "green" => format!("\x1b[32m{}\x1b[0m", text),
                "yellow" => format!("\x1b[33m{}\x1b[0m", text),
                "blue" => format!("\x1b[34m{}\x1b[0m", text),
                "magenta" => format!("\x1b[35m{}\x1b[0m", text),
                "cyan" => format!("\x1b[36m{}\x1b[0m", text),
                "gray" => format!("\x1b[90m{}\x1b[0m", text),
                "bold" => format!("\x1b[1m{}\x1b[0m", text),
                _ => text.to_string(),
            }
        } else {
            text.to_string()
        }
    }

    /// 格式化Span为树形结构
    fn format_span_tree(&self, spans: &[TraceSpan]) -> String {
        let mut output = String::new();
        
        // 按trace_id分组
        let mut traces_map = std::collections::HashMap::new();
        for span in spans {
            traces_map.entry(span.trace_id.clone())
                .or_insert_with(Vec::new)
                .push(span);
        }

        for (trace_id, trace_spans) in traces_map {
            output.push_str(&self.colorize(&format!("📊 Trace: {}\n", trace_id), "bold"));
            
            // 构建树形结构
            let tree_output = self.build_span_tree(trace_spans, 0);
            output.push_str(&tree_output);
            output.push('\n');
        }

        output
    }

    /// 递归构建Span树
    fn build_span_tree(&self, spans: Vec<&TraceSpan>, level: usize) -> String {
        let mut output = String::new();
        
        // 找到根节点
        let mut roots = Vec::new();
        let mut children_map: std::collections::HashMap<String, Vec<&TraceSpan>> = std::collections::HashMap::new();
        
        for span in &spans {
            if span.parent_span_id.is_none() {
                roots.push(*span);
            } else if let Some(parent_id) = &span.parent_span_id {
                children_map.entry(parent_id.clone())
                    .or_insert_with(Vec::new)
                    .push(*span);
            }
        }

        // 如果没有根节点，所有span都是根节点
        if roots.is_empty() {
            for span in &spans {
                roots.push(*span);
            }
        }

        for root in roots {
            output.push_str(&self.format_span_node(root, level));
            if let Some(children) = children_map.get(&root.span_id) {
                output.push_str(&self.build_span_tree(children.clone(), level + 1));
            }
        }

        output
    }

    /// 格式化单个Span节点
    fn format_span_node(&self, span: &TraceSpan, level: usize) -> String {
        let indent = "  ".repeat(level);
        let duration = self.format_duration(span.duration_ms());
        
        // 根据状态选择图标和颜色
        let (icon, color) = if span.tags.get("error").map(|v| v == "true").unwrap_or(false) {
            ("❌", "red")
        } else if span.is_finished() {
            ("✅", "green")
        } else {
            ("🔄", "yellow")
        };

        let mut output = format!(
            "{}{} {} {} [{}]\n",
            indent,
            icon,
            self.colorize(&span.operation_name, color),
            self.colorize(&format!("({})", duration), "gray"),
            self.colorize(&span.span_id[0..8.min(span.span_id.len())], "cyan")
        );

        if self.show_details {
            // 显示标签
            if !span.tags.is_empty() {
                for (key, value) in &span.tags {
                    output.push_str(&format!(
                        "{}  🏷️  {}: {}\n",
                        indent,
                        self.colorize(key, "blue"),
                        value
                    ));
                }
            }

            // 显示日志
            if !span.logs.is_empty() {
                for log in &span.logs {
                    output.push_str(&format!(
                        "{}  📝 {} {}\n",
                        indent,
                        self.colorize(&self.format_timestamp(log.timestamp), "gray"),
                        log.message
                    ));
                    
                    for (key, value) in &log.fields {
                        output.push_str(&format!(
                            "{}     {}: {}\n",
                            indent,
                            self.colorize(key, "magenta"),
                            value
                        ));
                    }
                }
            }
        }

        output
    }
}

impl Default for ConsoleExporter {
    fn default() -> Self {
        Self::new()
    }
}

#[async_trait]
impl TraceExporter for ConsoleExporter {
    async fn export(&self, spans: &[TraceSpan]) -> TracingResult<String> {
        if spans.is_empty() {
            return Ok(self.colorize("📊 No traces to display", "gray"));
        }

        let formatted = self.format_span_tree(spans);
        
        // 添加摘要信息
        let total_spans = spans.len();
        let finished_spans = spans.iter().filter(|s| s.is_finished()).count();
        let error_spans = spans.iter().filter(|s| s.tags.get("error").map(|v| v == "true").unwrap_or(false)).count();
        let total_duration: f64 = spans.iter().filter_map(|s| s.duration_ms()).sum();

        let summary = format!(
            "📈 Summary: {} spans ({} finished, {} errors) | Total: {}\n\n",
            self.colorize(&total_spans.to_string(), "bold"),
            self.colorize(&finished_spans.to_string(), "green"),
            if error_spans > 0 { self.colorize(&error_spans.to_string(), "red") } else { error_spans.to_string() },
            self.colorize(&self.format_duration(Some(total_duration)), "blue")
        );

        Ok(summary + &formatted)
    }

    fn name(&self) -> &str {
        "console"
    }

    fn supported_format(&self) -> TraceExportFormat {
        TraceExportFormat::Json // Console使用JSON作为基础格式
    }
}

/// 简化的Console导出器，仅显示基本信息
pub struct SimpleConsoleExporter;

impl SimpleConsoleExporter {
    pub fn new() -> Self {
        Self
    }
}

#[async_trait]
impl TraceExporter for SimpleConsoleExporter {
    async fn export(&self, spans: &[TraceSpan]) -> TracingResult<String> {
        let mut output = String::new();
        
        for span in spans {
            let duration = span.duration_ms()
                .map(|ms| format!("{:.2}ms", ms))
                .unwrap_or_else(|| "ongoing".to_string());
                
            let status = if span.tags.get("error").map(|v| v == "true").unwrap_or(false) {
                "ERROR"
            } else if span.is_finished() {
                "OK"
            } else {
                "RUNNING"
            };

            output.push_str(&format!(
                "[{}] {} {} ({})\n",
                status,
                span.operation_name,
                duration,
                &span.span_id[0..8.min(span.span_id.len())]
            ));
        }

        Ok(output)
    }

    fn name(&self) -> &str {
        "simple_console"
    }

    fn supported_format(&self) -> TraceExportFormat {
        TraceExportFormat::Json
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashMap;

    #[tokio::test]
    async fn test_console_exporter() {
        let exporter = ConsoleExporter::new();
        
        let span = TraceSpan {
            trace_id: "test-trace-123".to_string(),
            span_id: "test-span-456".to_string(),
            parent_span_id: None,
            operation_name: "test-operation".to_string(),
            start_time: std::time::SystemTime::now(),
            finish_time: Some(std::time::SystemTime::now()),
            tags: {
                let mut tags = HashMap::new();
                tags.insert("http.method".to_string(), "GET".to_string());
                tags
            },
            logs: vec![
                SpanLog {
                    timestamp: std::time::SystemTime::now(),
                    message: "Request started".to_string(),
                    fields: HashMap::new(),
                }
            ],
        };

        let result = exporter.export(&[span]).await;
        assert!(result.is_ok());
        
        let output = result.unwrap();
        assert!(output.contains("test-operation"));
        assert!(output.contains("Summary"));
        assert!(output.contains("✅")); // 完成图标
    }

    #[tokio::test]
    async fn test_console_exporter_with_error() {
        let exporter = ConsoleExporter::new();
        
        let span = TraceSpan {
            trace_id: "error-trace-123".to_string(),
            span_id: "error-span-456".to_string(),
            parent_span_id: None,
            operation_name: "error-operation".to_string(),
            start_time: std::time::SystemTime::now(),
            finish_time: Some(std::time::SystemTime::now()),
            tags: {
                let mut tags = HashMap::new();
                tags.insert("error".to_string(), "true".to_string());
                tags.insert("error.message".to_string(), "Test error".to_string());
                tags
            },
            logs: vec![],
        };

        let result = exporter.export(&[span]).await;
        assert!(result.is_ok());
        
        let output = result.unwrap();
        assert!(output.contains("error-operation"));
        assert!(output.contains("1")); // 错误统计
    }

    #[tokio::test]
    async fn test_simple_console_exporter() {
        let exporter = SimpleConsoleExporter::new();
        
        let span = TraceSpan {
            trace_id: "simple-trace-123".to_string(),
            span_id: "simple-span-456".to_string(),
            parent_span_id: None,
            operation_name: "simple-operation".to_string(),
            start_time: std::time::SystemTime::now(),
            finish_time: Some(std::time::SystemTime::now()),
            tags: HashMap::new(),
            logs: vec![],
        };

        let result = exporter.export(&[span]).await;
        assert!(result.is_ok());
        
        let output = result.unwrap();
        assert!(output.contains("[OK]"));
        assert!(output.contains("simple-operation"));
    }

    #[tokio::test]
    async fn test_console_exporter_no_color() {
        let exporter = ConsoleExporter::new().with_color(false);
        
        let span = TraceSpan {
            trace_id: "no-color-trace".to_string(),
            span_id: "no-color-span".to_string(),
            parent_span_id: None,
            operation_name: "no-color-operation".to_string(),
            start_time: std::time::SystemTime::now(),
            finish_time: Some(std::time::SystemTime::now()),
            tags: HashMap::new(),
            logs: vec![],
        };

        let result = exporter.export(&[span]).await;
        assert!(result.is_ok());
        
        let output = result.unwrap();
        // 确保没有ANSI颜色代码
        assert!(!output.contains("\x1b["));
    }
}